<?php
//$id$
?><?php
/**
 * collect data about form fields for ajax requests
 * @param array $data
 * 		'ref' - name of ajax item
 * 		'parent' - parent ajax item for current ajax item
 * 		'code' - js code if in this case ajax handling system doesn't work as needed
 * 		'trace_by' - Trace element by...
 * 		'trace_value' - Value for trace element
 * 		'event' - Event for action
 * 		'action' - Action when there is an event
 * 		'params' - additional parameters
 * 		'lib' - JS library in use.
 */
function collect($data = null) {
//	static $fields = array('includeLib' => array(), 'inlineJS' => array());
	static $fields = array();
	static $refs = array();

	if (empty($data)) { return $fields; }

	if (empty($data['trace_by'])) { $data['trace_by'] = false; }
	elseif (!checkTrace($data)) { throw new Exception('Unknown javascript trace method'); }

	if (empty($data['event'])) { $data['event'] = false; }
	elseif (!checkEvent($data)) { throw new Exception('Unknown javascript event'); }

	if (empty($data['action'])) { $data['action'] = false; }
	elseif (!checkAction($data)) { throw new Exception('Unknown javascript action '.$data['action']); }

	$data['trace'] = buildTrace($data);
	unset($data['trace_by'], $data['trace_value']);

	if (!empty($data['ref'])) {
		if (!empty($refs[$data['ref']])) {
			$refs[$data['ref']] = mergeJsData($refs[$data['ref']], $data); //TODO SOME PROCESS FUNCTION SHOULD BE HERE
		} else {
			$fields[$data['ref']] = $data;
			$refs[$data['ref']] = &$fields[$data['ref']];
		}

		if (!empty($data['parent'])) {
			if (!empty($refs[$data['parent']])) {
				if (empty($refs[$data['parent']]['children'][$data['ref']])) {
					$refs[$data['parent']]['children'][$data['ref']] = $refs[$data['ref']];
					unset($refs[$data['ref']]);
					unset($fields[$data['ref']]);
					$refs[$data['ref']] = &$refs[$data['parent']]['children'][$data['ref']];
				}
			} else {
				$fields[$data['parent']]['children'][$data['ref']] = $refs[$data['ref']];
				$refs[$data['parent']] = &$fields[$data['parent']];
				$refs[$data['ref']] = 0;
				unset($refs[$data['ref']]);
				unset($fields[$data['ref']]);
				$refs[$data['ref']] = &$fields[$data['parent']]['children'][$data['ref']];
			}
		}
	} elseif (!empty($data['parent'])) {
		if (!empty($refs[$data['parent']])) {
			$refs[$data['parent']]['children'][] = $data;
		} else {
			$fields[$data['parent']]['children'][] = $data;
			$refs[$data['parent']] = &$fields[$data['parent']];
		}
	}
}

function mergeJsData ($stack, $new) {
	static $countofChildrenArr = array();
	if (array_key_exists($new['ref'], $countofChildrenArr)) {
		$prevCCount = $countofChildrenArr[$new['ref']];
	} else { $prevCCount = 0; }
	$countofChildren = !empty($stack['children']) ? count($stack['children']) : 0;
	$countofChildrenArr[$new['ref']] = $countofChildren;

	if (!isset($stack['event'])) { $stack['event'] = $new['event']; }
	elseif (is_array($stack['event'])) { $stack['event'][] = $new['event']; }
	else { $stack['event'] = array($stack['event'], $new['event']); }
	unset($new['event']);

	if (!isset($stack['action'])) { $stack['action'] = $new['action']; }
	else {
		if ($stack['action'] === false) { $stack['action'] = array(0 => false); }
		elseif (!is_array($stack['action'])) { $stack['action'] = array($stack['action'] => $countofChildren); }
		else { //$stack['action'] is array
			end($stack['action']);
			list($key, $val) = each($stack['action']);
			if ($val !== false) { $stack['action'][$key] = $countofChildren - $prevCCount; }
		}
		if ($new['action'] === false) { $stack['action'][] = false; }
		else { $stack['action'][$new['action']] = 0; }
	}

	unset($new['action']);

	$stack  = array_merge($new, $stack);
	return $stack;
}


array(
	'key' => array(
		'event' => null,
		'action' => 'first',
		'children' => array(1,2,3),
	) // event is missing
);

//	'key' => array(
//		'action' => 'second',
//		'children' => array(4,5,6)
//	) // event is missing

array(
	'key' => array(
		'action' => array('first' => 3, 'second' => 0),
		'children' => array(1,2,3,4,5,6)

	) // event is missing
);


array(
	'id' => array(
		'event' => 'click',

////////////////////////////////
		'action' => 'sendData',
		'params' => array(),
////////////////////////////////
/////action = sendData//////////
////////////////////////////////
		'children' => array(

				'id_child' => array(
				'trace_by' => 'id',
				'trace_value' => 'id_child',
				'action' => 'val',
				'event' => 'collect', // NULL ?
			),
			'id_child2' => array(
				'trace_by' => 'id',
				'trace_value' => 'id_child2',
				'action' => 'val',
				'event' => 'collect', // NULL ?
			),
			'id_child3' => array(
				'trace_by' => 'id',
				'trace_value' => 'id_child3',
				'action' => 'val',
				'event' => 'collect', // NULL ?
			),
		),
////////////////////////////////
////end of action = sendData////
////////////////////////////////

////////////////////////////////
		'action' => 'css',
////////////////////////////////
/////////action = css///////////
////////////////////////////////
		'params' => array(
			'prop_name' => 'prop_value'
		),
////////////////////////////////
//////end of action = css///////
////////////////////////////////

////////////////////////////////
		'action' => 'attr',
////////////////////////////////
//////////action = attr/////////
////////////////////////////////
		'children' => array(
			0 => array(
				'action' => 'actionFunction',

				'children' => array(
					0 => array(
						'trace_by' => 'name',
						'trace_value' => 'test',
						'action' => 'actionName',
						'params' => array('key' => 'value')
					)
				)
			)
		)
////////////////////////////////
///////end of action = attr/////
////////////////////////////////

	),
	'object' => array(
		'action' => 'functionName',
		'children' => array(
			'childObject' => array(
				'action' => 'childFuncName',
				'params' => array(
					'key' => 'value'
				),
			),
			'child2Object' => array()					//second object action parameter is other object as is (event, action and some other parameters is empty)
		),
	)
);



//$('#id').								//trace
//	click(								//event
//		sendData(						//action
//			[							//children + params
//				$('#id_child').			//child trace
//					val(),				//child action (collect)
//				$('#id_child2').		//child trace
//					val(),				//child action (collect)
//				$('#id_child3').		//child trace
//					val()				//child action (collect)
//			]
//		)
//	).  ^ trace
//	css(								//action (event = collect)
//		'prop_name',					//params + children (empty children)
//		'prop_value'					//params + children (empty children)
//	).
//	attr(								//action (event = collect)
//		actionFunction(					//child action (event = collect, trace is empty)
//			$('attr[name=test]').		//child trace
//				actionName(				//child action
//					params... 			//child params if needed. Maybe other childs or just static data
//				)
//		)
//)
//
//object.								//trace (not JQuery(?))
//	functionName(						//action (event is empty)
//		childObject.					//param is child object
//			childFuncName(				//child object action
//				params...					//action params
//			)
//		,
//		child2Object					//second object action parameter is other object as is (event, action and some other parameters is empty)
//	)
//

array(
	'jsid' => array(
		'trace_by' => 'id',
		'trace_value' => 'jsid',
//		'event' => 'jsEvent',

		'event' => array('jsEvent', 'jsEvent2'),

//		'action' => 'jsAction',

		'action' => array('jsAction' => 3, 'jsAction2' => 0),

		'params' => array(
			'method' => 'post',
			'formaction' => '/address'
		),
		'children' => array(
			'child1' => array(),
			'child2' => array(),
			'child3' => array()
		)
	)
);

//jquery: $("#jsid").jsEvent(jsAction("post", "/address", ["child1":$("#child1"), "child2":$("#child2"), "child3":$("#child3")])).jsEvent2(jsAction2())



/*
 *
 * Usage example:
 *
 * collect(array('name' => 'login', 'action' => 'collect'));
 * collect(array('name' => 'pass', 'action' => 'collect'));
 * collect(array('name' => 'submit', 'action' => 'sendData',
 * 		'params' => array('method' => 'post', 'action' => '/user/login')));
 *
 */

function & getTraces() {
	static $traces = array(
				'jquery' => array(
					'id' => array(
						'prefix' => '$("#',
						'suffix' => '")'),
					'element' => array(
						'prefix' => '$("',
						'suffix' => '")'),
					'class' => array(
						'prefix' => '$(".',
						'suffix' => '")'),
					'selector' => array(
						'prefix' => '$("',
						'suffix' => '")'),
					'name' => array(
						'prefix' => '$("[name=',
						'suffix' => ']")'),
					'xpath' => array(
						'prefix' => '$("',
						'suffix' => '")')));
	return $traces;
}

//!trace.!event(!action)
//!trace == !trace_parent.!event_parent(!action_parent)
//

//$('#submit').click(sendData(!children))
// in this case !children is data from  submit child nodes
//
// and 'method' and 'action' from 'parameters' parameter

function & getEvents () {
	static $events = array(
		'jquery' => array(
			'collect' => '',
			'blur' => 1,
			'change' => array('code' => '.change(!action)'),
			'click' => array('code' => '.click(!action)'),
			'dblclick' => array('code' => '.dblclick(!action)'),
			'error' => array('code' => '.error(!action)'),
			'focus' => array('code' => '.focus(!action)'),
			'keydown' => array('code' => '.keydown(!action)'),
			'keypress' => array('code' => '.keypress(!action)'),
			'keyup' => array('code' => '.keyup(!action)'),
			'load' => array('code' => '.load(!action)'),
			'mousedown' => array('code' => '.mousedown(!action)'),
			'mousemove' => array('code' => '.mousemove(!action)'),
			'mouseout' => array('code' => '.mouseout(!action)'),
			'mouseover' => array('code' => '.mouseover(!action)'),
			'mouseup' => array('code' => '.mouseup(!action)'),
			'resize' => array('code' => '.resize(!action)'),
			'scroll' => array('code' => '.scroll(!action)'),
			'select' => array('code' => '.select(!action)'),
			'submit' => array('code' => '.submit(!action)'),
			'unload' => array('code' => '.unload(!action)'),
			'timer' => array('code' => '.timer(!action)'),
			'ajaxComplete' => array('code' => 'ajaxComplete(!action)'),
			'ajaxError' => array('code' => 'ajaxError(!action)'),
			'ajaxSend' => array('code' => 'ajaxSend(!action)'),
			'ajaxStart' => array('code' => 'ajaxStart(!action)'),
			'ajaxStop' => array('code' => 'ajaxStop(!action)'),
			'ajaxSuccess' => array('code' => 'ajaxSuccess(!action)'),
		)
	);
	return $events;
}

function checkEvent ($data) {
	$events = getEvents();
	if (empty($data['lib'])) { $data['lib'] = 'jquery'; }
	if (!array_key_exists($data['event'], $events[$data['lib']])) {
		return false;
	} else { return true; }
}

function & getActions() {
	static $actions = array(
		'jquery' => array(
			'collect' => array(),
			'sendData' => array(
				'before' => '',
				'code' => '$.!method(!url, !data, !callback, !type)',
				'after' => ''
		  	),
		  	'getHTML' => array('code' => ''),
			'getJSON' => array(
				'before' => '',
				'code' => 'getJSON( !url, !data, !callback )',
				'after' => ''
			)
		)
	);
	#collect - get value of element and send to server together with other data (default)
	#sendData - send data to server.
	#getHTML - get some data from server (required some additional parameters)
	#getJSON - get some data from server as JSON string (required some additional parameters)
	return $actions;
}

function checkAction ($data) {
	$actions = getActions();
	if (empty($data['lib'])) { $data['lib'] = 'jquery'; }
	if (!array_key_exists($data['action'], $actions[$data['lib']])) {
		return false;
	} else { return true; }
}

function buildAction ($data) {
	static $actions = null; if (empty($actions)) { $actions = getActions(); }
	$action = $actions[$data['action']]['code'];
	return strpos("!action", $data['trace']) !== FALSE ? str_replace("!action", $action, $data['trace']) : $action;
}

function checkTrace ($data) {
	$traces = getTraces();
	if (empty($data['lib'])) { $data['lib'] = 'jquery'; }
	if (!array_key_exists($data['trace_by'], $traces[$data['lib']])) {
		return false;
	} else { return true; }
}

function buildTrace ($data) {
	if (empty($data['trace_by'])) { return false; }
	$traces = getTraces();
	if (empty($data['lib'])) { $data['lib'] = 'jquery'; }
	return $traces[$data['lib']][$data['trace_by']]['prefix'].
		$data['trace_value'].
		$traces[$data['lib']][$data['trace_by']]['suffix'];
}

function includeLib ($data) {
	static $libs = array('jquery', 'prototype', 'dojo', 'raphael');

	if (empty($data['lib'])) { return $libs[0]; }
	else {
		$key = array_search($data['lib'], $libs);
		if ($key === false) {
			return $libs[0];
		}
		return $libs[$key];
	}
}

//function inlineJS ($data) {
//	static $traces = null; if (empty($traces)) { $traces = getTraces(); }
//	static $events = null; if (empty($events)) { $events = getEvents(); }
//
//	if (empty($data['trace'])) {
//		if (!empty($data['trace_by'])) {
//			$data['trace'] = $traces[$data['trace_by']]['preffix'];
//			if (!empty($data['trace_value'])) {
//				$data['trace'] .= $data['trace_value'].$traces[$data['trace_by']]['suffix'];
//			}
//		} else { $data['trace'] = ''; }
//	}
//	if (!empty($data['event'])) {
//		$data['trace'] = str_replace('!trace', $data['trace'], $data['event']);
//	}
//	if (!empty($data['action'])) {
//		$data['trace'] = buildAction($data);
//	}
//
//	unset($data['trace_by']);
//	unset($data['trace_value']);
//	unset($data['event']);
//	unset($data['action']);
//	return $data;
//}

//blur
////Bind a function to the blur event of each matched element.
//change()
////Triggers the change event of each matched element.
////Binds a function to the change event of each matched element.
//click()
////Triggers the click event of each matched element.
////Binds a function to the click event of each matched element.
//dblclick()
////Triggers the dblclick event of each matched element.
////Binds a function to the dblclick event of each matched element.
//error()
////Triggers the error event of each matched element.
////Binds a function to the error event of each matched element.
//focus()
////Triggers the focus event of each matched element.
////Binds a function to the focus event of each matched element.
//keydown()
////Triggers the keydown event of each matched element.
////Bind a function to the keydown event of each matched element.
//keypress()
////Triggers the keypress event of each matched element.
////Binds a function to the keypress event of each matched element.
//keyup()
////Triggers the keyup event of each matched element.
////Bind a function to the keyup event of each matched element.
//load(fn)
////Binds a function to the load event of each matched element.
//mousedown(fn)
////Binds a function to the mousedown event of each matched element.
//mousemove(fn)
////Bind a function to the mousemove event of each matched element.
//mouseout(fn)
////Bind a function to the mouseout event of each matched element.
//mouseover(fn)
////Bind a function to the mouseover event of each matched element.
//mouseup(fn)
////Bind a function to the mouseup event of each matched element.
//resize(fn)
////Bind a function to the resize event of each matched element.
//scroll(fn)
////Bind a function to the scroll event of each matched element.
//select()
////Trigger the select event of each matched element.
////Bind a function to the select event of each matched element.
//submit()
////Trigger the submit event of each matched element.
////Bind a function to the submit event of each matched element.
//unload(fn)
////Binds a function to the unload event of each matched element.
//ajaxComplete( callback ) 	Returns: jQuery
//Attach a function to be executed whenever an AJAX request completes. This is an Ajax Event.
//ajaxError( callback ) 	Returns: jQuery
//Attach a function to be executed whenever an AJAX request fails. This is an Ajax Event.
//ajaxSend( callback ) 	Returns: jQuery
//Attach a function to be executed before an AJAX request is sent. This is an Ajax Event.
//ajaxStart( callback ) 	Returns: jQuery
//Attach a function to be executed whenever an AJAX request begins and there is none already active. This is an Ajax Event.
//ajaxStop( callback ) 	Returns: jQuery
//Attach a function to be executed whenever all AJAX requests have ended. This is an Ajax Event.
//ajaxSuccess( callback )


?>
